package com.jiangyg.mall.authz.support.authentication.admin;

import com.jiangyg.mall.authz.constant.SecurityConstant;
import com.jiangyg.mall.authz.support.authentication.exception.AdminAuthenticationException;
import com.jiangyg.mall.authz.support.authentication.exception.TokenNotFoundException;
import com.jiangyg.mall.authz.support.authentication.exception.UnknowException;
import com.jiangyg.mall.authz.UserInfo;
import com.jiangyg.mall.authz.service.AdminAuthenticationService;
import com.jiangyg.mall.authz.support.TokenUtils;
import io.jsonwebtoken.ExpiredJwtException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;

/**
 * 类描述：访问权限校验
 *
 * @author jiangyg
 * @version 4.0
 * @date 2021-10-12
 */
@Slf4j
public class AdminAccessAuthenticationProvider implements AuthenticationProvider {

    /**
     * 后台管理权限认证相关实现
     */
    private final AdminAuthenticationService adminAuthenticationService;

    public AdminAccessAuthenticationProvider(AdminAuthenticationService adminAuthenticationService) {
        this.adminAuthenticationService = adminAuthenticationService;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // 1. 解析用户信息
        final Object token = authentication.getPrincipal();
        final UserInfo user = obtainUserInfo(token);
        // 2. 判断该令牌是否已退出登录（令牌的jti在缓存中存在则说明已退出登录）
        if (adminAuthenticationService.isLogouted(user)) {
            throw new AdminAuthenticationException(String.format("用户[%s]已退出登录！", user.getId()));
        }
        // 3. 校验通过则设置认证上下文
        ((AdminAccessAuthentication) authentication).setDetails(user);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        return authentication;
    }

    /**
     * 功能描述：获取认证用户信息
     *
     * @param token token
     * @return 用户信息
     */
    protected UserInfo obtainUserInfo(Object token) {
        if (token == null) {
            throw new TokenNotFoundException("找不到认证凭证token信息！");
        }
        try {
            return TokenUtils.parse(token.toString(), SecurityConstant.securityBase64(), UserInfo.class);
        } catch (ExpiredJwtException ex) {
            throw new CredentialsExpiredException("Token已过期！");
        } catch (Exception ex) {
            throw new UnknowException("解析token发生未知的异常", ex);
        }
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return AdminAccessAuthentication.class.isAssignableFrom(authentication);
    }

}
